package org.snake.spring.framework.context;

import org.snake.mvcframework.annotation.RAYAutowired;
import org.snake.mvcframework.annotation.RAYController;
import org.snake.mvcframework.annotation.RAYService;
import org.snake.spring.framework.beans.RAYBeanWrapper;
import org.snake.spring.framework.beans.config.RAYBeanDefinition;
import org.snake.spring.framework.beans.support.RAYBeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * Spring 容器的封装
 * <p>
 * 主要的职责:
 * 1.IoC
 * 2.DI
 * <p>
 * created by Ray
 */
public class RAYApplicationContext
{

    /**
     * 保存本地配置文件位置
     */
    private String[] configLocations;

    /**
     * 配置文件的读取器
     */
    private RAYBeanDefinitionReader reader;

    /**
     * 缓存所有从配置文件路径中，扫描加载出来的RAYBeanDefinition
     */
    private Map<String, RAYBeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * 实际上的IoC容器，缓存所有的RAYBeanWrapper
     */
    private Map<String, RAYBeanWrapper> factoryBeanInstanceCache = new HashMap<>();

    /**
     * 此Map对象缓存所有的单例对象，相当于二级缓存，当IoC容器中，使用BeanWrapper获取对象实例失败时，可以通过这个单例的二级缓存，重新获取到对象
     */
    private Map<String, Object> factoryBeanObjectCache = new HashMap<> ();

    /**
     * 构造函数
     *
     * @param configLocations 本地配置文件位置
     */
    public RAYApplicationContext(String... configLocations)
    {

        this.configLocations = configLocations;

        try
        {
            // 1.加载配置文件
            this.reader = new RAYBeanDefinitionReader(this.configLocations);
            // 2.配置文件中加载出来的beanDefinition集合
            List<RAYBeanDefinition> beanDefinitionList = this.reader.doLoadRAYBeanDefinitions();
            // 3.注册到BeanDefinitionMap
            doRegistryBeanDefinition(beanDefinitionList);
            // 4.创建对象，处理不需要延迟加载的类，创建IoC容器
            doCreateBean();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 创建对象，处理不需要延迟加载的类
     */
    private void doCreateBean()
    {

        // 迭代MAP，创建对象
        for (Map.Entry<String, RAYBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet())
        {
            // 类名或者是全类名
            String beanName = beanDefinitionEntry.getKey();
            // 1.创建对象
            // 2.依赖注入
            this.getBean(beanName);
        }
    }

    /**
     * 注册到BeanDefinitionMap
     *
     * @param beanDefinitionList beanDefinition集合
     */
    private void doRegistryBeanDefinition(List<RAYBeanDefinition> beanDefinitionList) throws Exception
    {

        for (RAYBeanDefinition beanDefinition : beanDefinitionList)
        {

            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName()))
            {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is exists!!");
            }
            // 注册到beanDefinitionMap
            // 1.注册首字母小写类名，默认支持getBean(String beanName)方法的调用获取
            this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            // 2.注册全类名，默认支持getBean(Class className)方法的调用获取
            this.beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);

        }
    }

    /**
     * 获取对象重载
     *
     * @param className 类对象的名称
     * @return Object
     */
    public Object getBean(Class className)
    {

        return getBean(className.getName());
    }

    /**
     * 获取对象
     *
     * @param beanName 类名称
     * @return Object
     */
    public Object getBean(String beanName)
    {

        // 1. 拿到beanName对应的配置信息，即BeanDefinition对象
        RAYBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

        // 2.实例化对象
        Object instance = instantiateBean(beanName, beanDefinition);

        // 3.将实例化后的对象封装成BeanWrapper
        RAYBeanWrapper beanWrapper = new RAYBeanWrapper(instance);

        // 4.将BeanWrapper缓存到IoC容器中
        factoryBeanInstanceCache.put(beanName, beanWrapper);

        // 5.完成依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);

        // 6.返回创建的实例对象
        return this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();

    }

    /**
     * 实例化对象
     *
     * @param beanName       类名称
     * @param beanDefinition Bean的定义类
     * @return Object
     */
    private Object instantiateBean(String beanName, RAYBeanDefinition beanDefinition)
    {
        // 全类名
        String className = beanDefinition.getBeanClassName();

        Object instance = null;

        try
        {

            // 通过全类名，反射获取类
            Class<?> clazz = Class.forName(className);
            // 创建类的实例
            instance = clazz.newInstance();

            // 如果这个对象被配置为AOP的扫描对象，就需要创建代理对象
            // TODO AOP接入(第四天完善)

            // 仿真Spring原生逻辑，保存所有单例对象
            // 当一级缓存IoC容器中，使用BeanWrapper获取对象实例失败时，可以通过这个单例的二级缓存，重新获取到对象
            this.factoryBeanObjectCache.put(beanName, instance);

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        // 返回对象
        return instance;

    }

    /**
     * 完成对象的依赖注入
     *
     * 扫描对象里面有没有注解，有注解就做依赖注入，没有就忽略
     *
     * @param beanName       类名称
     * @param beanDefinition Bean的定义类
     * @param beanWrapper    Bean的包装类
     */
    private void populateBean(String beanName, RAYBeanDefinition beanDefinition, RAYBeanWrapper beanWrapper)
    {

        // 拿到实例对象
        Object instance = beanWrapper.getWrapperInstance();
        // 拿到Class信息
        Class<?> clazz = beanWrapper.getWrapperClass();

        // 目前只处理 @RAYController 和 @RAYService
        if(!(clazz.isAnnotationPresent(RAYController.class) || clazz.isAnnotationPresent(RAYService.class)))
        {
            return;
        }

        // 循环声明的属性 包含了所有的 public/protected/private/default
        for (Field field : clazz.getDeclaredFields())
        {
            if (!field.isAnnotationPresent(RAYAutowired.class))
            {
                continue;
            }

            RAYAutowired autowired = field.getAnnotation(RAYAutowired.class);
            // 注解的名称
            String autowiredBeanName = autowired.value().trim();
            // 如果名称等于空，则默认用接口类型，进行注入
            if ("".equals(autowiredBeanName))
            {
                autowiredBeanName = field.getType().getName();
            }
            // 强制暴力访问
            field.setAccessible(true);
            // 从IoC容器中找到字段对应的对象，动态赋值
            try
            {
                // 如果没有需要注入的对象，则直接放弃依赖注入
                if(!this.factoryBeanInstanceCache.containsKey(autowiredBeanName))
                {
                    continue;
                }
                // 执行对象的依赖注入，从IoC容器中找到字段对应的对象，动态赋值
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }
        }

    }

    /**
     * 获取BeanDefinition容器中缓存的个数
     *
     * @return int 缓存数量
     */
    public int getBeanDefinitionCount()
    {

        return this.beanDefinitionMap.size();
    }

    /**
     * 获取BeanDefinition容器中缓存的所有BeanName
     *
     * @return String[]
     */
    public String[] getBeanDefinitionNames()
    {

        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    /**
     * 获取配置文件信息
     *
     * @return Properties
     */
    public Properties getConfiguration()
    {

        return this.reader.getConfiguration();
    }

}
